package de.pass.ch.graph;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;

import de.pass.ch.graph.edge.Edge;
import de.pass.ch.graph.node.INode;
import de.pass.ch.graph.node.SearchNode;

public class SearchGraph implements IGraph, Serializable {

	private static final long serialVersionUID = -2383539169918415087L;

	private SearchNode[] nodes;
	private Edge[] edges;

	public SearchGraph(UpdateableGraph graph) {
		construct(graph);
	}

	public SearchGraph(SearchNode[] nodes, Edge[] edges) {
		this.nodes = nodes;
		this.edges = edges;
	}

	@Override
	public INode node(int index) {
		return nodes[index];
	}

	@Override
	public Edge edge(int nodeID, int edgeID) {
		return edges[edgeID];
	}

	@Override
	public int noOfNodes() {
		return nodes.length - 1;
	}

	@Override
	public int noOfEdges() {
		return edges.length;
	}

	@Override
	public int firstLevelEdge(int nodeID) {
		return nodes[nodeID].getFirstLevelEdge();
	}

	@Override
	public int lastEdge(int nodeID) {
		return nodes[nodeID + 1].getFirstLevelEdge();
	}

	private void construct(UpdateableGraph graph) {

		nodes = new SearchNode[graph.noOfNodes() + 1];
		Edge[] edgetmp = new Edge[graph.noOfEdges()];

		int currFLE = 0;
		for (int i = 0; i < nodes.length - 1; i++) {

			nodes[i] = new SearchNode(currFLE);

			for (int e = graph.firstLevelEdge(i); e < graph.lastEdge(i); e++) {
				edgetmp[currFLE++] = graph.edge(i, e);
				graph.deleteEdge(i, e);
			}

			graph.deleteNode(i);

		}

		graph = null;

		System.gc();

		edges = new Edge[currFLE];

		for (int i = 0; i < edges.length; i++) {
			edges[i] = edgetmp[i];
			edgetmp[i] = null;
		}

		edgetmp = null;

		System.gc();

		nodes[nodes.length - 1] = new SearchNode(currFLE);

	}

	// public void serialize(OutputStream out) {
	//
	// long start = System.currentTimeMillis();
	//
	// ObjectOutputStream oOut = null;
	//
	// try {
	//
	// oOut = new ObjectOutputStream(out);
	// oOut.writeObject(this);
	//
	// } catch (IOException e) {
	// e.printStackTrace();
	// oOut = null;
	// } finally {
	// try {
	// oOut.close();
	// } catch (Exception e2) {
	// oOut = null;
	// }
	// }
	//
	// System.out.println("\nSerializing of SearchGraph took "
	// + (System.currentTimeMillis() - start) + " ms");
	//
	// }

	// public static SearchGraph deserialize(InputStream in) {
	//
	// System.out.println("Deserializing SearchGraph .....");
	//
	// long start = System.currentTimeMillis();
	//
	// ObjectInputStream oIn = null;
	// SearchGraph res = null;
	//
	// try {
	//
	// oIn = new ObjectInputStream(in);
	// res = (SearchGraph) oIn.readObject();
	//
	// } catch (IOException e) {
	// e.printStackTrace();
	// oIn = null;
	// } catch (ClassNotFoundException e) {
	// e.printStackTrace();
	// } finally {
	// try {
	// if (oIn != null)
	// oIn.close();
	// } catch (Exception e2) {
	// oIn = null;
	// }
	// }
	//
	// System.out.println("\nDeserializing of SearchGraph took "
	// + (System.currentTimeMillis() - start) + " ms");
	//
	// return res;
	//
	// }

	public void serializeFast(FileOutputStream out) {

		System.out.println("Serializing SearchGraph .....");

		long start = System.currentTimeMillis();

		int nodeSize = nodes.length;
		int edgeSize = edges.length;

		int bytes = 0;

		FileChannel fc = null;

		try {

			fc = out.getChannel();

			ByteBuffer bb = ByteBuffer.allocate(4).order(
					ByteOrder.LITTLE_ENDIAN);
			bb.clear();

			bb.putInt(nodeSize);
			bb.flip();
			bytes += fc.write(bb);
			bb.clear();

			bb.putInt(edgeSize);
			bb.flip();
			bytes += fc.write(bb);
			bb.clear();

			bb = ByteBuffer.allocate(4 * 10000).order(ByteOrder.LITTLE_ENDIAN);
			bb.clear();

			for (int i = 0; i < nodeSize; i++) {

				if (i % 10000 == 0 && i > 0) {

					bb.flip();
					bytes += fc.write(bb);
					bb.clear();

					if (nodeSize - i < 10000) {

						bb = ByteBuffer.allocate(4 * (nodeSize - i)).order(
								ByteOrder.LITTLE_ENDIAN);
						bb.clear();
					}
				}

				bb.putInt(nodes[i].getFirstLevelEdge());

			}

			if (bb.position() > 0) {
				bb.flip();
				bytes += fc.write(bb);
			}

			System.out.println("vor edges: " + fc.position());

			bb = ByteBuffer.allocate(16 * 10000).order(ByteOrder.LITTLE_ENDIAN);
			bb.clear();

			for (int i = 0; i < edgeSize; i++) {

				if (i % 10000 == 0 && i > 0) {

					bb.flip();
					bytes += fc.write(bb);
					bb.clear();

					if (edgeSize - i < 10000) {

						bb = ByteBuffer.allocate(16 * (edgeSize - i)).order(
								ByteOrder.LITTLE_ENDIAN);
						bb.clear();
					}
				}

				bb.putInt(edges[i].getRealTarget());
				bb.putInt(edges[i].getRealWeight());
				bb.putInt(edges[i].getRealshortcutMiddle());
				bb.putInt(edges[i].getRealshortcutOriginalEdgeCount());

			}

			if (bb.position() > 0) {
				bb.flip();
				bytes += fc.write(bb);
			}

			System.out.println(bytes + " bytes written");

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fc != null)
					fc.close();
			} catch (Exception e) {
			}
		}

		System.out.println("Serializing of SearchGraph took "
				+ (System.currentTimeMillis() - start) + " ms");

	}

	public void deserializeFast(FileInputStream in) {

		this.nodes = null;
		this.edges = null;

		int bytes = 0;

		System.gc();

		System.out.println("Deserializing SearchGraph .....");

		long start = System.currentTimeMillis();

		FileChannel fc = null;

		try {

			ByteBuffer bb = ByteBuffer.allocate(4).order(
					ByteOrder.LITTLE_ENDIAN);
			bb.clear();

			fc = in.getChannel();

			bytes += fc.read(bb);
			bb.flip();
			int nodeSize = bb.getInt();

			bb.clear();

			bytes += fc.read(bb);
			bb.flip();
			int edgeSize = bb.getInt();

			SearchNode[] nodes = new SearchNode[nodeSize];

			bb = ByteBuffer.allocate(4 * 10000).order(ByteOrder.LITTLE_ENDIAN);

			for (int i = 0; i < nodeSize; i++) {

				if (i % 10000 == 0) {

					if (nodeSize - i < 10000) {
						bb = ByteBuffer.allocate(4 * (nodeSize - i)).order(
								ByteOrder.LITTLE_ENDIAN);
						bb.clear();
					}

					bb.clear();
					bytes += fc.read(bb);
					bb.flip();
				}

				int firstLevelEdge = bb.getInt();

				nodes[i] = new SearchNode(firstLevelEdge);

			}

			Edge[] edges = new Edge[edgeSize];

			bb = ByteBuffer.allocate(16 * 10000).order(ByteOrder.LITTLE_ENDIAN);

			for (int i = 0; i < edgeSize; i++) {

				if (i % 10000 == 0) {

					if (edgeSize - i < 10000) {

						bb = ByteBuffer.allocate(16 * (edgeSize - i)).order(
								ByteOrder.LITTLE_ENDIAN);
						bb.clear();
					}

					bb.clear();
					bytes += fc.read(bb);
					bb.flip();
				}

				int target = bb.getInt();
				int weight = bb.getInt();
				int shortcutMiddle = bb.getInt();
				int shortcutOriginalEdgeCount = bb.getInt();

				edges[i] = new Edge(target, weight, shortcutMiddle,
						shortcutOriginalEdgeCount);

			}

			System.out.println(bytes + " bytes read");

			this.nodes = nodes;
			this.edges = edges;

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fc != null)
					fc.close();
			} catch (Exception e) {
			}
		}

		System.out.println("Deserializing of SearchGraph took "
				+ (System.currentTimeMillis() - start) + " ms");

	}

}
